package com.yc.cloud.admin.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yc.cloud.admin.common.UmsAdminConstants;
import com.yc.cloud.admin.dto.request.UmsAdminParam;
import com.yc.cloud.admin.dto.request.UmsAdminUpdateParam;
import com.yc.cloud.admin.dto.request.UpdateAdminPasswordParam;
import com.yc.cloud.admin.service.UmsAdminCacheService;
import com.yc.cloud.admin.service.UmsAdminService;
import com.yc.cloud.admin.service.UmsResourceCacheService;
import com.yc.cloud.common.basic.dto.BasePageReqDTO;
import com.yc.cloud.common.basic.exception.Asserts;
import com.yc.cloud.common.basic.utils.RequestUtil;
import com.yc.cloud.entity.UmsAdmin;
import com.yc.cloud.entity.UmsAdminLoginLog;
import com.yc.cloud.entity.UmsResource;
import com.yc.cloud.mapper.UmsAdminLoginLogMapper;
import com.yc.cloud.mapper.UmsAdminMapper;
import com.yc.cloud.mapper.UmsResourceMapper;
import com.yc.cloud.security.dto.AdminUserDetails;
import com.yc.cloud.security.utils.AdminUtils;
import com.yc.cloud.security.utils.JwtTokenUtil;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.springframework.beans.BeanUtils;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;

/**
 * <p>
 * 后台用户表 服务实现类
 * </p>
 *
 * @author JackGu
 * @since 2022-09-11
 */
@Service
@Slf4j
public class UmsAdminServiceImpl extends ServiceImpl<UmsAdminMapper, UmsAdmin> implements UmsAdminService {

    @Resource
    private UmsAdminCacheService adminCacheService;
    @Resource
    private UmsResourceCacheService umsResourceCacheService;
    @Resource
    private JwtTokenUtil jwtTokenUtil;

    @Resource
    private PasswordEncoder passwordEncoder;

    @Resource
    private UmsAdminMapper umsAdminMapper;

    @Resource
    private UmsAdminLoginLogMapper umsAdminLoginLogMapper;

    @Resource
    private UmsResourceMapper resourceMapper;


    @Override
    public IPage<UmsAdmin> list(BasePageReqDTO queryParam) {
        val query = new QueryWrapper<UmsAdmin>();
        IPage<UmsAdmin> page;
        //排序
        if (StrUtil.isNotEmpty(queryParam.getSortBy())) {
            if (BasePageReqDTO.ORDER_DESC.equals(queryParam.getOrder())) {
                query.orderByDesc(UmsAdmin.ID);
            } else if (BasePageReqDTO.ORDER_ASC.equals(queryParam.getOrder())) {
                query.orderByAsc(UmsAdmin.ID);
            }
        }
        //模糊查询
        val keyword = queryParam.getKeyword();
        if (StrUtil.isNotEmpty(keyword)) {
            query.and(queryWrapper -> {
                queryWrapper.like(UmsAdmin.USERNAME, keyword).or().like(UmsAdmin.NICK_NAME, keyword);
            });
        }
        //分页
        if (queryParam.getPage()) {
            page = new Page<>(queryParam.getPageNum(), queryParam.getPageSize(), true);
        } else {
            page = new Page<>(queryParam.getPageNum(), umsAdminMapper.selectCount(query), true);
        }
        return umsAdminMapper.selectPage(page, query);
    }

    @Override
    public String queryByUserNameAndPassword(String username, String password) {
        String token = null;
        //密码需要客户端加密后传递
        try {
            adminCacheService.delByUserName(username);
            UserDetails userDetails = loadUserByUsername(username);
            if (!passwordEncoder.matches(password, userDetails.getPassword())) {
                Asserts.fail("密码不正确");
            }
            if (!userDetails.isEnabled()) {
                Asserts.fail("帐号已被禁用");
            }
            UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
            SecurityContextHolder.getContext().setAuthentication(authentication);
            token = jwtTokenUtil.generateToken(userDetails);
            updateLoginTimeByUsername(username);
            insertLoginLog(username);
        } catch (AuthenticationException e) {
            log.warn("登录异常:{}", e.getMessage());
        }
        return token;
    }

    @Override
    public UserDetails loadUserByUsername(String username) {
        //获取用户信息
        UmsAdmin admin = getAdminByUsername(username);
        if (admin != null) {
            List<UmsResource> resourceList = getResourceList(admin);
            return new AdminUserDetails(admin, resourceList);
        }
        throw new UsernameNotFoundException("用户名或密码错误");
    }

    @Override
    public UmsAdmin queryByUserName(String username) {
        val query = new QueryWrapper<UmsAdmin>();
        query.eq("username", username);
        return umsAdminMapper.selectOne(query);
    }

    @Override
    public UmsAdmin getAdminByUsername(String username) {
        UmsAdmin admin = adminCacheService.getByUserName(username);
        if (admin != null) {
            return admin;
        }
        val queryMap = new HashMap<String, Object>(1);
        queryMap.put("username", username);
        List<UmsAdmin> adminList = umsAdminMapper.selectByMap(queryMap);
        if (adminList != null && adminList.size() > 0) {
            admin = adminList.get(0);
            adminCacheService.setByUserName(username, admin);
            return admin;
        }
        return null;
    }

    @Override
    public UmsAdmin getAdmin() {
        val username = AdminUtils.getCurrentUserName();
        UmsAdmin admin = adminCacheService.getByUserName(username);
        if (admin != null) {
            return admin;
        }
        val queryMap = new HashMap<String, Object>(1);
        queryMap.put("username", username);
        List<UmsAdmin> adminList = umsAdminMapper.selectByMap(queryMap);
        if (adminList != null && adminList.size() > 0) {
            admin = adminList.get(0);
            adminCacheService.setByUserName(username, admin);
            return admin;
        }
        return null;
    }

    @Override
    public List<UmsResource> getResourceList(UmsAdmin umsAdmin) {
        List<UmsResource> resourceList = umsResourceCacheService.getByUserName(umsAdmin.getUsername());
        if (CollUtil.isNotEmpty(resourceList)) {
            return resourceList;
        }
        //TODO 待测试
        resourceList = resourceMapper.getResourceList(umsAdmin.getId());
        if (CollUtil.isNotEmpty(resourceList)) {
            umsResourceCacheService.setByUserName(umsAdmin.getUsername(), resourceList);
        }
        return resourceList;
    }

    @Override
    public UmsAdmin register(UmsAdminParam umsAdminParam) {
        UmsAdmin umsAdmin = new UmsAdmin();
        BeanUtils.copyProperties(umsAdminParam, umsAdmin);
        umsAdmin.setCreateTime(LocalDateTime.now());
        umsAdmin.setStatus(UmsAdminConstants.STATUS_ENABLE);
        //将密码进行加密操作
        String encodePassword = passwordEncoder.encode(umsAdmin.getPassword());
        umsAdmin.setPassword(encodePassword);
//        umsAdmin.setIcon(icon);
        umsAdminMapper.insert(umsAdmin);
        return umsAdmin;
    }

    @Override
    public void logout() {
        adminCacheService.delByUserName(AdminUtils.getCurrentUserName());
        umsResourceCacheService.delByUserName(AdminUtils.getCurrentUserName());
    }

    @Override
    public int updatePassword(UpdateAdminPasswordParam param) {
        if (StrUtil.isEmpty(param.getUsername()) || StrUtil.isEmpty(param.getOldPassword()) || StrUtil.isEmpty(param.getNewPassword())) {
            return -1;
        }
        val query = new QueryWrapper<UmsAdmin>();
        query.eq("username", param.getUsername());
        val adminList = umsAdminMapper.selectList(query);
        if (CollUtil.isEmpty(adminList)) {
            return -2;
        }
        UmsAdmin umsAdmin = adminList.get(0);
        if (!passwordEncoder.matches(param.getOldPassword(), umsAdmin.getPassword())) {
            return -3;
        }
        umsAdmin.setPassword(passwordEncoder.encode(param.getNewPassword()));
        umsAdminMapper.updateById(umsAdmin);
        adminCacheService.del(umsAdmin.getId());
        return 1;
    }

    @Override
    public int update(Long id, UmsAdminUpdateParam admin) {
        UmsAdmin rawAdmin = umsAdminMapper.selectById(id);
        if (rawAdmin == null) {
            return -1;
        }
        if (rawAdmin.getPassword().equals(admin.getPassword())) {
            //与原加密密码相同的不需要修改
            rawAdmin.setPassword(null);
        } else {
            //与原加密密码不同的需要加密修改
            if (StrUtil.isEmpty(admin.getPassword())) {
                rawAdmin.setPassword(null);
            } else {
                rawAdmin.setPassword(passwordEncoder.encode(admin.getPassword()));
            }
        }
        rawAdmin.setEmail(admin.getEmail());
        rawAdmin.setTrueName(admin.getTrueName());
        rawAdmin.setIcon(admin.getIcon());
        rawAdmin.setNote(admin.getNote());
        int count = umsAdminMapper.updateById(rawAdmin);
        adminCacheService.del(id);
        return count;
    }

    @Override
    public String refreshToken(String oldToken) {
        return jwtTokenUtil.refreshHeadToken(oldToken);
    }

    @Override
    public UmsAdmin create(UmsAdmin param) {
        val umsAdmin = new UmsAdmin();
        BeanUtils.copyProperties(param, umsAdmin);
        umsAdmin.setCreateTime(LocalDateTime.now());
        umsAdmin.setStatus(UmsAdminConstants.STATUS_ENABLE);
        //将密码进行加密操作
        String encodePassword = passwordEncoder.encode(umsAdmin.getPassword());
        umsAdmin.setPassword(encodePassword);
        int count = umsAdminMapper.insert(umsAdmin);
        if (count > 0) {
            adminCacheService.del(umsAdmin.getId());
            return umsAdmin;
        }
        return null;
    }


    /**
     * 根据用户名修改登录时间
     */
    private void updateLoginTimeByUsername(String username) {
        val admin = queryByUserName(username);
        if (admin != null) {
            admin.setLoginTime(LocalDateTime.now());
            umsAdminMapper.updateById(admin);
        }
    }

    private void insertLoginLog(String username) {
        UmsAdmin admin = getAdminByUsername(username);
        if (admin == null) {
            return;
        }
        UmsAdminLoginLog loginLog = new UmsAdminLoginLog();
        loginLog.setAdminId(admin.getId());
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attributes != null) {
            HttpServletRequest request = attributes.getRequest();
            loginLog.setIp(RequestUtil.getRequestIp(request));
        }
        umsAdminLoginLogMapper.insert(loginLog);
    }
}
